package org.wheel.plugins.toolkit.jdbc.pool.util;

import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.lang.Pair;
import cn.hutool.core.util.PageUtil;
import cn.hutool.core.util.StrUtil;
import lombok.extern.slf4j.Slf4j;
import org.wheel.module.core.util.Page;
import org.wheel.plugins.toolkit.jdbc.pool.bean.DatabaseConnection;
import org.wheel.plugins.toolkit.jdbc.pool.util.excution.SqlPageExecutor;
import org.wheel.plugins.toolkit.jdbc.pool.util.excution.SqlQueryExecutor;
import org.wheel.plugins.toolkit.jdbc.pool.util.excution.SqlTransactionExecutor;
import org.wheel.plugins.toolkit.jdbc.pool.util.excution.SqlUpdateExecutor;
import org.wheel.plugins.toolkit.sql.enums.SqlModel;
import org.wheel.plugins.toolkit.util.sql.SQLHelper;

import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

/**
 * SQL 执行门面工具类
 * 只保留一行调用的 public 方法，实现全部下沉到专属 Executor
 */
@Slf4j
public final class SqlExecutionUtil {

    /* ---------- 单例 ---------- */
    private static final SqlExecutionUtil INSTANCE = new SqlExecutionUtil();

    private SqlExecutionUtil() {
    }

    public static SqlExecutionUtil getInstance() {
        return INSTANCE;
    }

    /* ---------- 专属执行器 ---------- */
    private final SqlQueryExecutor query  = new SqlQueryExecutor();
    private final SqlPageExecutor page   = new SqlPageExecutor();
    private final SqlUpdateExecutor     update = new SqlUpdateExecutor();
    private final SqlTransactionExecutor tx     = new SqlTransactionExecutor();

    /* ================== 查询 ================== */

    /**
     * 一次性查询所有数据，内部分页查询，返回所有结果。
     * 注意，尽量不要查询大数据量数据
     * @param conn 数据库连接
     * @param countSql 总量统计查询
     * @param querySql 分页查询
     * @param param 参数
     * @param model SQL 语法模式
     * @return List结果集
     */
    public List<Map<String, Object>> executeSqlQueryForListAll(DatabaseConnection conn,
                                                            String countSql,
                                                            String querySql,
                                                            LinkedHashMap<String, Object> param,
                                                            SqlModel model) {
        int pageSize = 1000;
        long count = executeCountSqlQuery(conn, countSql, param, model);
        if(count <= pageSize){
            return query.queryForList(conn, querySql, param, model);
        }else{
            int totalPages = PageUtil.totalPage(count, pageSize);
            return IntStream.rangeClosed(1, totalPages).boxed()
                    .flatMap(pageNo-> {
                        String finalSql = page.rebuildPageSql(querySql, pageNo, pageSize);
                        return executeSqlQueryForList(conn, finalSql, param, model).stream();
                    }).collect(Collectors.toList());
        }
    }

    /**
     * 一次性查询所有数据，内部分页查询，返回所有结果。
     * 注意，尽量不要查询大数据量数据
     * @param conn 数据库连接
     * @param countSql 总量统计查询
     * @param querySql 分页查询
     * @param param 参数
     * @param model SQL 语法模式
     * @param clazz 返回结果类型
     * @return List结果集
     * @param <T> 返回结果类型
     */
    public <T> List<T> executeSqlQueryForListAll(DatabaseConnection conn,
                                                 String countSql,
                                                 String querySql,
                                                 LinkedHashMap<String, Object> param,
                                                SqlModel model,
                                                Class<T> clazz) {
        int pageSize = 1000;
        long count = executeCountSqlQuery(conn, countSql, param, model);
        if(count <= pageSize){
            return query.queryForList(conn, querySql, param, model, clazz);
        }else{
            int totalPages = PageUtil.totalPage(count, pageSize);
            return IntStream.rangeClosed(1, totalPages).boxed()
                    .flatMap(pageNo-> {
                        String finalSql = page.rebuildPageSql(querySql, pageNo, pageSize);
                        return executeSqlQueryForList(conn, finalSql, param, model, clazz).stream();
                    }).collect(Collectors.toList());
        }
    }

    public List<Map<String, Object>> executeSqlQueryForList(DatabaseConnection conn,
                                                            String sql,
                                                            LinkedHashMap<String, Object> param,
                                                            SqlModel model) {
        return query.queryForList(conn, sql, param, model);
    }

    public <T> List<T> executeSqlQueryForList(DatabaseConnection conn,
                                              String sql,
                                              LinkedHashMap<String, Object> param,
                                              SqlModel model,
                                              Class<T> clazz) {
        return query.queryForList(conn, sql, param, model, clazz);
    }

    public Map<String, Object> executeSqlQueryForMap(DatabaseConnection conn,
                                                     String sql,
                                                     LinkedHashMap<String, Object> param,
                                                     SqlModel model) {
        return query.queryForMap(conn, sql, param, model);
    }

    public <T> T executeSqlQueryForObject(DatabaseConnection conn,
                                          String sql,
                                          LinkedHashMap<String, Object> param,
                                          SqlModel model,
                                          Class<T> clazz) {
        return query.queryForObject(conn, sql, param, model, clazz);
    }

    /* ================== 分页 ================== */

    public Page<Map<String, Object>> executePageSqlQuery(DatabaseConnection conn,
                                                         String pageSql,
                                                         LinkedHashMap<String, Object> param,
                                                         SqlModel model) {
        return page.queryPage(conn, pageSql, param, model);
    }

    public Page<Map<String, Object>> executePageSqlQuery(DatabaseConnection conn,
                                                         String pageSql,
                                                         LinkedHashMap<String, Object> param,
                                                         long pageNo,
                                                         long pageSize,
                                                         SqlModel model) {
        return page.queryPage(conn, pageSql, param, pageNo, pageSize, model);
    }

    public Page<Map<String, Object>> executePageSqlQuery(DatabaseConnection conn,
                                                         String countSql,
                                                         String pageSql,
                                                         LinkedHashMap<String, Object> param,
                                                         long pageNo,
                                                         long pageSize,
                                                         SqlModel model) {
        return page.queryPage(conn, countSql, pageSql, param, pageNo, pageSize, model);
    }

    public <T> Page<T> executePageSqlQuery(DatabaseConnection conn,
                                           String pageSql,
                                           LinkedHashMap<String, Object> param,
                                           long pageNo,
                                           long pageSize,
                                           SqlModel model,
                                           Class<T> elementType) {
        return page.queryPage(conn, pageSql, param, pageNo, pageSize, model, elementType);
    }

    public <T> Page<T> executePageSqlQuery(DatabaseConnection conn,
                                           String countSql,
                                           String pageSql,
                                           LinkedHashMap<String, Object> param,
                                           long pageNo,
                                           long pageSize,
                                           SqlModel model,
                                           Class<T> elementType) {
        return page.queryPage(conn, countSql, pageSql, param, pageNo, pageSize, model, elementType);
    }

    /* ================== 更新 ================== */

    public int executeSqlUpdate(DatabaseConnection conn,
                                String sql,
                                LinkedHashMap<String, Object> param,
                                SqlModel model) {
        return update.update(conn, sql, param, model);
    }

    public int executeSqlUpdate(DatabaseConnection conn, String sql, Object... args) {
        return update.nativeUpdate(conn, sql, args);
    }

    public int executeSqlUpdate(DatabaseConnection conn, String sql) {
        return update.nativeUpdate(conn, sql);
    }

    /* ================== 事务-批量 ================== */

    public int[] batchExecute(DatabaseConnection conn, String sql, List<Object[]> batchArgs) {
        return tx.batchSameSql(conn, sql, batchArgs);
    }
    public int[] namedParameterBatchExecute(DatabaseConnection conn, String sql, List<Map<String, Object>> batchArgs) {
        return tx.namedParameterBatchSameSql(conn, sql, batchArgs);
    }

    /**
     * 如果返回null，说明执行成功，如果返回非null，则返回错误信息
     * @param conn 连接信息
     * @param batchPairs 批量执行
     * @return 错误信息
     */
    public String batchExecute(DatabaseConnection conn,
                               Collection<Pair<String, Object[]>> batchPairs) {
        return tx.batchInTransaction(conn, batchPairs);
    }

    /**
     * 如果返回null，说明执行成功，如果返回非null，则返回错误信息
     * @param conn 连接信息
     * @param batchPairs 批量执行
     * @return 错误信息
     */
    public String namedParameterBatchExecute(DatabaseConnection conn,
                               Collection<Pair<String, Map<String, Object>>> batchPairs) {
        return tx.namedParameterBatchInTransaction(conn, batchPairs);
    }

    /* ================== 原生兼容 ================== */

    public List<Map<String, Object>> executeNativeQueryForList(DatabaseConnection conn,
                                                               String sql,
                                                               Object[] params) {
        return query.nativeQueryForList(conn, sql, params);
    }

    public Map<String, Object> executeNativeQueryForMap(DatabaseConnection conn,
                                                        String sql,
                                                        Object[] params) {
        return query.nativeQueryForMap(conn, sql, params);
    }

    public <T> List<T> executeNativeQueryForList(DatabaseConnection conn,
                                                 String sql,
                                                 Object[] params,
                                                 Class<T> clazz) {
        return query.nativeQueryForList(conn, sql, params, clazz);
    }

    public <T> T executeNativeQueryForObject(DatabaseConnection conn,
                                             String sql,
                                             Object[] params,
                                             Class<T> clazz) {
        return query.nativeQueryForObject(conn, sql, params, clazz);
    }

    /* ================== 统计总数 ================== */

    public Long executeCountSqlQuery(DatabaseConnection conn,
                                     String countSql,
                                     LinkedHashMap<String, Object> params,
                                     SqlModel model) {
        return query.queryForObject(conn, countSql, params, model, Long.class);
    }
}